// Copyright (c) 2012, yuno. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
package sugarmintcandy.widgetwindow;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;

public class WindowFrameAnimation extends View {
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mLayoutParams;
    private Rect mStartRect;
    private Rect mEndRect;
    private long mStartTime;
    private int mDuration;
    private boolean mRunning;
    private Paint mPaint1;
    private Paint mPaint2;
    private OnFinishListener mOnFinishListener;
    private Interpolator mInterpolator;
    private int mStrokeWidth;

    public interface OnFinishListener {
	void onFinish(WindowFrameAnimation animation);
    }

    public WindowFrameAnimation(Context context) {
	super(context);
	mWindowManager = (WindowManager) context
		.getSystemService(Context.WINDOW_SERVICE);
	mLayoutParams = new WindowManager.LayoutParams();
	mLayoutParams.x = 0;
	mLayoutParams.y = 0;
	mLayoutParams.width = LayoutParams.MATCH_PARENT;
	mLayoutParams.height = LayoutParams.MATCH_PARENT;
	mLayoutParams.gravity = Gravity.CENTER;
	mLayoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
		| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
		| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
		| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
		| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
		| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
	mLayoutParams.alpha = 1;
	mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
	mLayoutParams.packageName = context.getPackageName();
	mLayoutParams.format = PixelFormat.TRANSLUCENT;
	mInterpolator = new AccelerateDecelerateInterpolator();
    }

    public void start(Rect startRect, Rect endRect, int duration, int color,
	    OnFinishListener onFinish) {
	stop();
	mStartRect = startRect;
	mEndRect = endRect;
	mStartTime = Long.MIN_VALUE;
	mDuration = duration;
	mWindowManager.addView(this, mLayoutParams);
	mStrokeWidth = getResources().getDimensionPixelSize(
		R.dimen.frame_animation_width);
	mPaint1 = new Paint();
	mPaint1.setColor(getDarkerColor(color));
	mPaint1.setStyle(Paint.Style.STROKE);
	mPaint1.setStrokeWidth(mStrokeWidth);
	mPaint2 = new Paint();
	mPaint2.setColor(getBrighterColor(color));
	mPaint2.setStyle(Paint.Style.STROKE);
	mPaint2.setStrokeWidth(mStrokeWidth);
	mOnFinishListener = onFinish;
	mRunning = true;
	invalidate();
    }

    public void stop() {
	if (mRunning) {
	    mWindowManager.removeView(this);
	    mRunning = false;
	}
    }

    @Override
    protected void onDraw(Canvas canvas) {
	if (!mRunning) {
	    return;
	}
	long now = SystemClock.uptimeMillis();
	if (mStartTime == Long.MIN_VALUE) {
	    mStartTime = now;
	}
	int t = (int) (now - mStartTime);
	if (t < 0 || t >= mDuration) {
	    t = mDuration;
	}

	Rect rect;
	rect = lerp(mStartRect, mEndRect, t, mDuration + 40);
	rect.offset(mStrokeWidth / 2, mStrokeWidth / 2);
	canvas.drawRect(rect, mPaint1);
	rect.offset(-mStrokeWidth, -mStrokeWidth);
	canvas.drawRect(rect, mPaint2);

	rect = lerp(mStartRect, mEndRect, t + 20, mDuration + 40);
	rect.offset(mStrokeWidth / 2, mStrokeWidth / 2);
	canvas.drawRect(rect, mPaint1);
	rect.offset(-mStrokeWidth, -mStrokeWidth);
	canvas.drawRect(rect, mPaint2);

	rect = lerp(mStartRect, mEndRect, t + 40, mDuration + 40);
	rect.offset(mStrokeWidth / 2, mStrokeWidth / 2);
	canvas.drawRect(rect, mPaint1);
	rect.offset(-mStrokeWidth, -mStrokeWidth);
	canvas.drawRect(rect, mPaint2);

	if (t < mDuration) {
	    postInvalidate();
	} else if (mOnFinishListener != null) {
	    mWindowManager.removeView(this);
	    mRunning = false;
	    mOnFinishListener.onFinish(this);
	    mOnFinishListener = null;
	}
    }

    private Rect lerp(Rect a, Rect b, int numer, int denom) {
	float t = (float) numer / denom;
	t = mInterpolator.getInterpolation(t);
	Rect c = new Rect();
	c.left = Math.round((b.left - a.left) * t) + a.left;
	c.top = Math.round((b.top - a.top) * t) + a.top;
	c.right = Math.round((b.right - a.right) * t) + a.right;
	c.bottom = Math.round((b.bottom - a.bottom) * t) + a.bottom;
	return c;
    }

    private int getBrighterColor(int color) {
	int red = (color >> 16) & 255;
	int green = (color >> 8) & 255;
	int blue = (color >> 0) & 255;
	int alpha = 128;
	red = 255 - (255 - red) / 2;
	green = 255 - (255 - green) / 2;
	blue = 255 - (255 - blue) / 2;
	return (alpha << 24) | (red << 16) | (green << 8) | blue;
    }

    private int getDarkerColor(int color) {
	int red = (color >> 16) & 255;
	int green = (color >> 8) & 255;
	int blue = (color >> 0) & 255;
	int alpha = 128;
	red = red / 2;
	green = green / 2;
	blue = blue / 2;
	return (alpha << 24) | (red << 16) | (green << 8) | blue;
    }
}
